﻿using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;
using Nemerle.Assertions;
using System;
using System.Collections.Generic;
using System.Linq;
using Nemerle2.Compiler;
using Microsoft.Cci;
using Nemerle;

namespace MetadataReader.CCI
{
  internal partial class CciVariantTypeInfo : CciTypeInfo, IVariantTypeInfo
  {
    public static CreateCciVariantOptionTypeInfo(
      provider : CciMetadataProvider,
      typeDef  : INamedTypeDefinition,
      assembly : CciReferencedAssembly
      ) : CciTypeInfo
    {
      CciVariantOptionTypeInfo(provider, typeDef, assembly)
    }

    private class CciVariantOptionTypeInfo : CciTypeInfo, IVariantOptionTypeInfo
    {
      private static VariantOptionCodeNotInit               = -1;
      private static IsNotVariantOption                     = -42;

      // Это поле видно только классу CciVariantTypeInfo, так текущий класс (CciVariantOptionTypeInfo)
      // является вложенным в него private-классом.
      internal mutable _variantOptionCode : int              = VariantOptionCodeNotInit;

      public this(provider : CciMetadataProvider, typeDef : INamedTypeDefinition, assembly : CciReferencedAssembly)
      {
        base(provider, typeDef, assembly);
      }

      /// <summary>
      /// Gets the unique code of variant option
      /// </summary>
      /// <value>
      /// Unique code of variant option
      /// </value>
      public VariantOptionCode : int
      {
        get
        {
          assert(_variantOptionCode == VariantOptionCodeNotInit || _variantOptionCode == IsNotVariantOption || _variantOptionCode >= 0);

          when (_variantOptionCode == VariantOptionCodeNotInit)
          {
            if (DeclaringType == null || !(DeclaringType is CciVariantTypeInfo))
              _variantOptionCode = IsNotVariantOption;
            else
            {
              // TryInitializeVariantOptions() may not set value of _variantOptionCode if nested type
              // not variant option. Set it to IsNotVariantOption by default.
              _variantOptionCode = IsNotVariantOption;
              (DeclaringType :> CciVariantTypeInfo).TryInitializeVariantOptions();
              assert(_variantOptionCode != VariantOptionCodeNotInit);
            }
          }

          _variantOptionCode
        }
      }

      /// <summary>
      /// Gets field of constant object associated with this type info whether the type info is constant variant definition
      /// </summary>
      [Memoize]
      public GetConstantObject() : ValueOption[IFieldInfo]
      {
        if(!IsVariantOption)
          ValueOption.None()
        else
          TryGetField(NBindingFlags.Public | NBindingFlags.Static, f => f.Name == "_N_constant_object")
      }

      public override IsVariantOption : bool { get { true } }
    }
  }
}
